package com.mhc.haval.security.manager;

import com.mhc.haval.security.model.RestGrantedAuthority;
import com.mhc.haval.security.constant.Authorities;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;

/**
 * @author Churry
 * @create 2017-08-16 17:25
 **/
@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {

    /**
     * 重写决策方法，用户拥有的权限与请求访问所需要的权限对比
     *
     * @param authentication
     * @param object
     * @param configAttributes
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        //如果是允许所有人访问的url，直接验证成功
        for (String permitAllUrl : Authorities.PERMITALL_URL) {
            if (matchers(permitAllUrl, request)) {
                return;
            }
        }
        //如果是允许所有人访问的资源，springsecurity会把请求标记为匿名，可现在登录后这个方法有问题
        if (Authorities.ANONYMOUS_USER.equals(authentication.getPrincipal())) {
            return;
        } else {
            //拿到用户拥有的权限，与访问资源需要的权限做匹配
            for (GrantedAuthority hasAuthority : authentication.getAuthorities()) {
                if (hasAuthority instanceof RestGrantedAuthority) {
                    RestGrantedAuthority restGrantedAuthority = (RestGrantedAuthority) hasAuthority;
                    if (restGrantedAuthority.getUrl() != null && matchers(restGrantedAuthority.getUrl(), request)) {
                        if (restGrantedAuthority.getMethod().equals(request.getMethod()) || restGrantedAuthority.getMethod().equals(Authorities.ALL_METHOD)) {
                            return;
                        }
                    }
                }
            }
            throw new AccessDeniedException(Authorities.NO_AUTHORITY);
        }
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    public boolean matchers(String url, HttpServletRequest request) {
        AntPathRequestMatcher matcher = new AntPathRequestMatcher(url);
        if (matcher.matches(request)) {
            return true;
        }
        return false;
    }
}
